package lee.vioson.ebook.ui.components

import androidx.compose.foundation.Image
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.calculatePan
import androidx.compose.foundation.gestures.calculateZoom
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.gestures.rememberTransformableState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.positionChanged
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.unit.IntSize
import java.lang.Float.max
import java.lang.Float.min

/**
 * 放大倍数 a: 2倍, 放大倍数 b: 容器宽/图片宽、容器长/图片长的较大值.
 * 双击: 放大到倍数 min(a,b), 缩小到原始尺寸
 * 双指: 放大到倍数 max(a,b), 缩小到原始尺寸
 */
@Composable
fun PhotoView(painter: Painter, click: (() -> Unit)? = null) {
    var imageInitialSize by remember {
        mutableStateOf(IntSize.Zero)
    }
    var boxSize by remember {
        mutableStateOf(IntSize.Zero)
    }

    var maxScale by remember {
        mutableStateOf(1f)
    }
    var scale by remember {
        mutableStateOf(1f)
    }
    var offset by remember {
        mutableStateOf(Offset.Zero)
    }
    // 使用 transformable 进行缩放、平移和旋转, 但是都需要双指, 不太适用图片放大后的平移(通常是单指)
    val transformState = rememberTransformableState { zoomChange, _, _ ->
        scale = (scale * zoomChange).coerceIn(1f, maxScale)

        // 1. 直接使用平移距离
        // offset += panChange

        // 2. 考虑图片超出容器边界问题
        /*var x = offset.x
        if (scale * imageInitialSize.width > boxSize.width) {
            val delta = scale * imageInitialSize.width - boxSize.width
            x = (offset.x + panChange.x).coerceIn(-delta / 2, delta / 2)
        }
        var y = offset.y
        if (scale * imageInitialSize.height > boxSize.height) {
            val delta = scale * imageInitialSize.height - boxSize.height
            y = (offset.y + panChange.y).coerceIn(-delta / 2, delta / 2)
        }
        offset = Offset(x, y)*/
    }

    Box(modifier = Modifier
        .fillMaxSize()
        .graphicsLayer {
            scaleX = scale
            scaleY = scale
            translationX = offset.x
            translationY = offset.y
        }
        // 1. 使用 transformable 处理缩放和平移
        //.transformable(transformState)
        // 2. 使用 awaitPointerEventScope 自行处理缩放和平移
        .pointerInput(Unit) {
            awaitEachGesture {
                do {
                    val event = awaitPointerEvent()
                    val zoomChange = event.calculateZoom()
                    // LogUtils.d("zoomChange: $zoomChange")
                    scale *= zoomChange

                    var panChange = event.calculatePan()
                    // LogUtils.d("panChange: $panChange")
                    // 乘以 scale 防止在图片放大状态下移动得特别慢
                    panChange *= scale

                    // 为了让放大倍数超过 maxScale 时还可以随手势继续放大,
                    // 但是要手松开后回到 maxScale 这个状态, 在下面 awaitPointerEventScope 结束后
                    // 执行了 scale.coerceIn(1f, maxScale).
                    // 因此这个地方也要临时使用最后的真实值来计算 offset, 否则会出现整个图片上下 offset
                    // 不一致的情况(放大到超过 maxScale 后再松手)
                    val tempScale = scale.coerceIn(1f, maxScale)
                    var x = offset.x
                    if (tempScale * imageInitialSize.width > boxSize.width) {
                        val delta = tempScale * imageInitialSize.width - boxSize.width
                        x = (offset.x + panChange.x).coerceIn(-delta / 2, delta / 2)
                    }
                    var y = offset.y
                    if (tempScale * imageInitialSize.height > boxSize.height) {
                        val delta = tempScale * imageInitialSize.height - boxSize.height
                        y = (offset.y + panChange.y).coerceIn(-delta / 2, delta / 2)
                    }

                    if (x != offset.x || y != offset.y) {
                        offset = Offset(x, y)
                    }

                    event.changes.forEach {
                        if (it.positionChanged()) {
                            it.consume()
                        }
                    }
                } while (event.changes.any { it.pressed })
            }
//            forEachGesture {
//                awaitPointerEventScope {
//                    do {
//                        val event = awaitPointerEvent()
//                        val zoomChange = event.calculateZoom()
//                        // LogUtils.d("zoomChange: $zoomChange")
//                        scale *= zoomChange
//
//                        var panChange = event.calculatePan()
//                        // LogUtils.d("panChange: $panChange")
//                        // 乘以 scale 防止在图片放大状态下移动得特别慢
//                        panChange *= scale
//
//                        // 为了让放大倍数超过 maxScale 时还可以随手势继续放大,
//                        // 但是要手松开后回到 maxScale 这个状态, 在下面 awaitPointerEventScope 结束后
//                        // 执行了 scale.coerceIn(1f, maxScale).
//                        // 因此这个地方也要临时使用最后的真实值来计算 offset, 否则会出现整个图片上下 offset
//                        // 不一致的情况(放大到超过 maxScale 后再松手)
//                        val tempScale = scale.coerceIn(1f, maxScale)
//                        var x = offset.x
//                        if (tempScale * imageInitialSize.width > boxSize.width) {
//                            val delta = tempScale * imageInitialSize.width - boxSize.width
//                            x = (offset.x + panChange.x).coerceIn(-delta / 2, delta / 2)
//                        }
//                        var y = offset.y
//                        if (tempScale * imageInitialSize.height > boxSize.height) {
//                            val delta = tempScale * imageInitialSize.height - boxSize.height
//                            y = (offset.y + panChange.y).coerceIn(-delta / 2, delta / 2)
//                        }
//
//                        if (x != offset.x || y != offset.y) {
//                            offset = Offset(x, y)
//                        }
//
//                        event.changes.forEach {
//                            if (it.positionChanged()) {
//                                it.consume()
//                            }
//                        }
//                    } while (event.changes.any { it.pressed })
//                }
//
//                scale = scale.coerceIn(1f, maxScale)
//                if (scale == 1f) {
//                    offset = Offset.Zero
//                }
//            }
        }
        .pointerInput(Unit) {
            detectTapGestures(
                onDoubleTap = {
                    offset = Offset.Zero
                    scale = if (scale != 1.0f) {
                        1.0f
                    } else {
                        min(2f, maxScale)
                    }
                }, onTap = {
                    click?.invoke()
                }
            )
        }
        .onSizeChanged {
            // LogUtils.d("Box onSizeChanged")
            boxSize = it
            val xRatio = it.width.toFloat() / imageInitialSize.width
            val yRatio = it.height.toFloat() / imageInitialSize.height
            maxScale = max(2f, max(xRatio, yRatio))
        },
        contentAlignment = Alignment.Center
    ) {
        Image(
            modifier = Modifier.onSizeChanged {
                // LogUtils.d("Image onSizeChanged")
                imageInitialSize = it
            }, painter = painter, contentDescription = ""
        )
    }
}